package maze;

import java.awt.*;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.util.LinkedList;
import java.util.List;
import java.util.Stack;
import maze.levels.Knowledge;
import maze.levels.Question;
import sun.font.Font2D;

/**
 *
 * @author Dennis Nijssen
 * @author Loki
 */
public class Maze {
    private final int rowAmount;
    private final int columnAmount;
    private double scaleX, scaleY;
    private Cell[][] cells;
    private Stack<Cell> pathTaken;
    public static Color backColor = Color.GRAY;
    
    public Maze(int rowAmount, int columnAmount, int width, int height) {
        this.rowAmount = rowAmount;
        this.columnAmount = columnAmount;
        scaleX = (double)(width - 1) / rowAmount;
        scaleY = (double)(height - 1) / columnAmount;
        
        // Initialize a new two-dimensional array of Cells
        cells = new Cell[rowAmount][columnAmount];
        for(int i = 0; i < rowAmount; i++) {
            for(int j = 0; j < columnAmount; j++) {
                cells[i][j] = new Cell(i, j);
            }
        }
        
        generateMaze();
    }
    
    private void generateMaze() {
        int totalCells = rowAmount * columnAmount;
        int cellsVisited = 0;
        Cell currentCell;
        Cell newCell;
        List<Cell> adjCells;
        
        pathTaken = new Stack<Cell>();
        
        // Begin cell
        currentCell = cells[0][0];
        
        while(cellsVisited < totalCells) {
            adjCells = getAdjacentCells(currentCell);
            if(adjCells.size() > 0) {
                newCell = adjCells.get((int)(Math.random() * adjCells.size()));
                switch(Direction.getDirection(currentCell.getRow(), currentCell.getColumn(),
                        newCell.getRow(), newCell.getColumn())) {
                    case NORTH: 
                        currentCell.setNorth(false);
                        newCell.setSouth(false);
                        break;
                    case SOUTH:
                        currentCell.setSouth(false);
                        newCell.setNorth(false);
                        break;
                    case EAST:
                        currentCell.setEast(false);
                        newCell.setWest(false);
                        break;
                    case WEST:
                        currentCell.setWest(false);
                        newCell.setEast(false);
                        break;
                }
                pathTaken.push(currentCell);
                currentCell = newCell;
                currentCell.setVisited(true);
                cellsVisited++;
            } else {
                if(pathTaken.size() > 0) {
                    currentCell = pathTaken.pop();
                }
            }
        }
    }
    
    private List<Cell> getAdjacentCells(Cell cell) {
        List<Cell> adjacentCells = new LinkedList<Cell>();
        Cell c;
        
        if(cell.getRow() > 0) {
            c = cells[cell.getRow() - 1][cell.getColumn()];
            if(c.getVisited() == false)
                adjacentCells.add(c);
        }
        
        if(cell.getRow() < (rowAmount - 1)) {
            c = cells[cell.getRow() + 1][cell.getColumn()];
            if(c.getVisited() == false)
                adjacentCells.add(c);
        }
            
        if(cell.getColumn() > 0) {
            c = cells[cell.getRow()][cell.getColumn() - 1];
            if(c.getVisited() == false)
                adjacentCells.add(c);
        }
        
        if(cell.getColumn() < (columnAmount - 1)) {
            c = cells[cell.getRow()][cell.getColumn() + 1];
            if(c.getVisited() == false)
                adjacentCells.add(c);
        }
        
        return adjacentCells;
    }
    
    public Cell getCellAt(int row, int column) {
        if(row >= 0 && row <= (rowAmount - 1) && column >= 0 && column <= (columnAmount - 1))
            return cells[row][column];
        else
            return null;
    }
    
    public int getColumnAmount() {
        return columnAmount;
    }
    
    public int getRowAmount() {
        return rowAmount;
    }
    
    public double getScaleX() {
        return scaleX;
    }
    
    public double getScaleY() {
        return scaleY;
    }
    
    public void putQuestions(List<Question> questions) {
        if(questions == null) return;
        
        for(Question q : questions) {
            // Als vraag al beantwoord is, ga door naar de volgende vraag
            if(q.getVisited()) continue;
            
            Cell randomCell = null;
            
            // Get a random cell to put a question
            while(randomCell == null || randomCell.hasQuestion() || randomCell.hasKnowledge()) {
//                randomCell = pathTaken.get((int)(Math.random() * pathTaken.size())); 
                randomCell = getCellAt((int)(Math.random() * rowAmount), 
                    (int)(Math.random() * columnAmount));
            }

            randomCell.addQuestion(q);
        }
    }
    public void putKnowledge(List<Knowledge> knowledge) {
        if(knowledge.isEmpty()) return;
        
        for(Knowledge piece : knowledge) {
            Cell randomCell;
            
            // Get a random cell to put knowledge
            do {
                randomCell = getCellAt((int)(Math.random() * rowAmount), 
                    (int)(Math.random() * columnAmount));
            } while((randomCell == null || randomCell.hasQuestion() || randomCell.hasKnowledge()));
            
            randomCell.addKnowledge(piece);
        }
    }
    
    public void paintCell(Graphics2D g2d, int i, int j) {
        // Snel :D
        if(i == (rowAmount - 1) && j == (columnAmount - 1)) {
            g2d.setColor(Color.orange);
            //g2d.fill(new Rectangle2D.Double(scaleX(i) + 1, scaleY(j) + 1, scaleX - 2, scaleY - 2));
        }
        else {
            g2d.setColor(backColor);
            //g2d.fill(new Rectangle2D.Double(scaleX(i) + 1, scaleY(j) + 1, scaleX - 2, scaleY - 2));
        }
        
        
        int x = 0, y = 0;
        if(i == (rowAmount - 1)) {
            x = 2;
        }
        if(j == (columnAmount - 1)) {
            y = 2;
        }
        g2d.fill(new Rectangle2D.Double(scaleX(i), scaleY(j), scaleX - x, scaleY - y));

        if(cells[i][j].getNorth()) drawWallHorizontal(g2d, scaleX(i), scaleY(j), scaleX, 10);
        if(cells[i][j].getSouth()) drawWallHorizontal(g2d, scaleX(i), scaleY(j + 1), scaleX, 10);
        if(cells[i][j].getWest()) drawWallVertical(g2d, scaleX(i), scaleY(j), scaleY, 10);
        if(cells[i][j].getEast()) drawWallVertical(g2d, scaleX(i + 1), scaleY(j), scaleY, 10);
        
        if(i == (rowAmount - 1) && j == (columnAmount - 1)) {
            g2d.setColor(Color.BLACK);
            Font font = new Font(Font.MONOSPACED, Font.PLAIN, 12);
            g2d.setFont(font);
            g2d.drawString("stap",
                    (int)scaleX(i), (int)(scaleY(j) + (scaleY(j + 1) - scaleY(j)) / 2));
            g2d.drawString((Game.level + 1) + "/" + Game.levels.size(),
                    (int)scaleX(i), (int)(scaleY(j) + (scaleY(j + 1) - scaleY(j)) / 2) + 12);
            //g2d.fill(new Rectangle2D.Double(scaleX(i) + 1, scaleY(j) + 1, scaleX - 2, scaleY - 2));
        }
           
        
        if(cells[i][j].hasQuestion()) cells[i][j].getQuestion().paint(g2d, scaleX(i), scaleY(j), scaleX, scaleY);
        if(cells[i][j].hasKnowledge()) cells[i][j].getKnowledge().paint(g2d, scaleX(i), scaleY(j), scaleX, scaleY);
    }
    
    public void paint(Graphics2D g2d) {
        
        
        
        // Calculating the scale to paint
        for(int i = 0; i < rowAmount; i++) {
            for(int j = 0; j < columnAmount; j++) {
                //Image offscreenImage = createImage(getWidth(), getHeight());
                //Graphics offscreenGraphics = offscreenImage.getGraphics();
        
                //Graphics2D g2d = (Graphics2D)offscreenGraphics;
                //g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 
                //RenderingHints.VALUE_ANTIALIAS_ON);
                
                paintCell(g2d, i, j);
                
                //g2D.drawImage(offscreenImage, (int)scaleX(i), (int)scaleY(j), this);
            }
        }
    }
    
    private void drawWallHorizontal(Graphics2D g2d, double x, double y, double w, double h) {
        g2d.setColor(Color.green);
        
        int trees = (int)(w / h);
        double treeSize = (w / trees);
        for(int i = 0; i < trees; i++) {
            g2d.fill(new Ellipse2D.Double(x + (i * treeSize), y - (treeSize / 2), treeSize, treeSize));
        }
    }
    
    private void drawWallVertical(Graphics2D g2d, double x, double y, double w, double h) {
        g2d.setColor(Color.green);
        
        int trees = (int)(w / h);
        double treeSize = (w / trees);
        for(int i = 0; i < trees; i++) {
            g2d.fill(new Ellipse2D.Double(x - (treeSize / 2), y + (i * treeSize), treeSize, treeSize));
        }
    }
    
    public double scaleX(int scalable) {
        return scaleX * scalable;
    }
    
    public double scaleY(int scalable) {
        return scaleY * scalable;
    }
}